﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Ingenious.Graph;

namespace Ingenious.Core
{
    #region Supporting Classes
    /// <summary>
    /// Represents a URI namespace (with optional prefix name) for triple URIs
    /// </summary>
    public class NamespaceUri
    {
        #region Constants
        private const string HttpWwwPrefix = "http://www.";
        private const string HttpPrefix = "http://";
        #endregion

        private readonly Uri m_namespaceUri;
        private readonly string m_namespacePrefix;

        #region Object Lifetime
        public NamespaceUri(string namespaceUriString)
        {
            if (string.IsNullOrEmpty(namespaceUriString))
            {
                throw new ArgumentException("namespaceUriString");
            }

            Uri namespaceUri = null;
            if (!Uri.TryCreate(namespaceUriString, UriKind.Absolute, out namespaceUri))
            {
                throw new ArgumentException("namespaceUri");
            }

            m_namespaceUri = namespaceUri;
            m_namespacePrefix = GenerateNamespacePrefix(m_namespaceUri.ToString());
        }

        public NamespaceUri(string namespaceUriString, string namespacePrefix)
        {
            if (string.IsNullOrEmpty(namespaceUriString))
            {
                throw new ArgumentException("namespaceUriString");
            }

            if (string.IsNullOrEmpty(namespacePrefix))
            {
                throw new ArgumentException("namespacePrefix");
            }

            Uri namespaceUri = null;
            if (!Uri.TryCreate(namespaceUriString, UriKind.Absolute, out namespaceUri))
            {
                throw new ArgumentException("namespaceUri");
            }

            m_namespaceUri = namespaceUri;
            m_namespacePrefix = namespacePrefix;
        }

        public NamespaceUri(Uri namespaceUri, string namespacePrefix)
        {
            if (namespaceUri == null)
            {
                throw new ArgumentNullException("namespaceUri");
            }

            m_namespaceUri = namespaceUri;
            m_namespacePrefix = namespacePrefix;
        }

        public NamespaceUri(Uri namespaceUri)
        {
            if (namespaceUri == null)
            {
                throw new ArgumentNullException("namespaceUri");
            }

            m_namespaceUri = namespaceUri;
            m_namespacePrefix = GenerateNamespacePrefix(namespaceUri.ToString());
        }
        #endregion

        public override string ToString()
        {
            return "Uri: " + m_namespaceUri.ToString() + ", Prefix: " + m_namespacePrefix;
        }

        private static string GenerateNamespacePrefix(string namespaceUriString)
        {
            string namespacePrefix = string.Empty;

            if (namespaceUriString.StartsWith(HttpWwwPrefix))
            {
                namespacePrefix = namespaceUriString.Substring(HttpWwwPrefix.Length, 2).ToLower();
            }
            else if (namespaceUriString.StartsWith(HttpPrefix))
            {
                namespacePrefix = namespaceUriString.Substring(HttpPrefix.Length, 2);
            }

            return namespacePrefix;
        }

        public bool IsMatch(Uri absoluteUri)
        {
            if (absoluteUri == null)
            {
                throw new ArgumentNullException("absoluteUri");
            }

            return IsMatch(absoluteUri.ToString());
        }

        public bool IsMatch(string absoluteUriString)
        {
            if (absoluteUriString == null)
            {
                throw new ArgumentNullException("absoluteUriString");
            }

            return absoluteUriString.StartsWith(Uri.ToString());
        }

        public static bool TryGetRelativeUri(NamespaceUri namespaceUri, Uri absoluteUri, out string relativeUri)
        {
            return TryGetRelativeUri(namespaceUri, absoluteUri.ToString(), out relativeUri);
        }

        public static bool TryGetRelativeUri(NamespaceUri namespaceUri, string absoluteUriString, out string relativeUri)
        {
            relativeUri = null;

            if (!absoluteUriString.StartsWith(namespaceUri.Uri.ToString()))
            {
                return false;
            }

            string matchingNamespaceUriString = namespaceUri.Uri.ToString();
            int startIndex = matchingNamespaceUriString.Length;
            int relativeUriLength = absoluteUriString.Length - startIndex;

            relativeUri = absoluteUriString.Substring(startIndex, relativeUriLength);

            return true;
        }

        #region Object Override
        public override int GetHashCode()
        {
            return m_namespaceUri.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            NamespaceUri namespaceUri = obj as NamespaceUri;

            if (namespaceUri != null)
            {
                return namespaceUri.Uri.Equals(Uri);
            }

            return false;
        }
        #endregion

        #region Properties
        public Uri Uri
        {
            get { return m_namespaceUri; }
        }

        public string NamespacePrefix
        {
            get { return m_namespacePrefix; }
        }
        #endregion
    }
    #endregion
    
    /// <summary>
    /// Defines the URI prefix for dynamically accessed node properties
    /// Use this when getting/setting node properties
    /// </summary>
    
    // TODO: Allow use of an expression to resolve the URI from the property name
    public class NamespaceUriScope : IDisposable
    {
        private readonly string m_namespaceUriString;
        private readonly NamespaceUri m_namespaceUri;

        private volatile bool m_diposed;

        #region Object Lifetime
        public NamespaceUriScope(string namespaceUriString)
        {
            if (namespaceUriString == null)
            {
                throw new ArgumentNullException("namespaceUriString");
            }

            Uri uri = null;
            if (!Uri.TryCreate(namespaceUriString, UriKind.Absolute, out uri))
            {
                throw new ArgumentException("'" + namespaceUriString + "' is not a valid URI");
            }

            // Use the processed URI stirng
            m_namespaceUriString = uri.ToString();
            m_namespaceUri = new NamespaceUri(uri);

            // Register the scope for this thread
            Node.RegisterNamespaceScope(this);
        }

        public NamespaceUriScope(NamespaceUri namespaceUri)
        {
            if (namespaceUri == null)
            {
                throw new ArgumentNullException("namespaceUri");
            }

            m_namespaceUriString = namespaceUri.Uri.ToString();
            m_namespaceUri = namespaceUri;

            // Register the scope for this thread
            Node.RegisterNamespaceScope(this);
        }

        public void Dispose()
        {
            if (!m_diposed)
            {
                Node.UnregisterUriPrefixScope(this);
                m_diposed = true;
            }
        }
        #endregion

        #region Properties
        public string NamespaceUriString
        {
            get { return m_namespaceUriString; }
        }

        public NamespaceUri NamespaceUri
        {
            get { return m_namespaceUri; }
        }
        #endregion
    }
}
